package com.shuwen.gcdj.service.account;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.shuwen.gcdj.bean.AddResp;
import com.shuwen.gcdj.bean.VoidResp;
import com.shuwen.gcdj.bean.dept.response.DeptListItemResp;
import com.shuwen.gcdj.bean.member.request.*;
import com.shuwen.gcdj.bean.member.response.*;
import com.shuwen.gcdj.bean.power.response.MenuTreeResp;
import com.shuwen.gcdj.bean.power.response.PowerItemResp;
import com.shuwen.gcdj.bean.power.response.SuperPowerCacheResp;
import com.shuwen.gcdj.bean.role.response.PowersAndMenus;
import com.shuwen.gcdj.bean.role.response.RoleItemResp;
import com.shuwen.gcdj.bean.role.response.SuperRoleCacheResp;
import com.shuwen.gcdj.common.api.CommonPage;
import com.shuwen.gcdj.common.api.CommonResult;
import com.shuwen.gcdj.common.api.IErrorCode;
import com.shuwen.gcdj.common.constant.Constants;
import com.shuwen.gcdj.common.constant.MemberCst;
import com.shuwen.gcdj.common.constant.RolePowerCst;
import com.shuwen.gcdj.common.enums.*;
import com.shuwen.gcdj.common.error.ErrorReport;
import com.shuwen.gcdj.common.exception.ApiException;
import com.shuwen.gcdj.common.sysLog.AddSysLogUtil;
import com.shuwen.gcdj.common.util.FrpStringUtil;
import com.shuwen.gcdj.common.util.account.AccountUtil;
import com.shuwen.gcdj.common.util.crypt.CryptTool;
import com.shuwen.gcdj.common.util.crypt.JWTUtil;
import com.shuwen.gcdj.common.util.serialize.SerializeUtils;
import com.shuwen.gcdj.common.util.string.StringUtil;
import com.shuwen.gcdj.common.util.time.TimeUtil;
import com.shuwen.gcdj.dto.account.InitUserDto;
import com.shuwen.gcdj.dto.account.LoginReqDto;
import com.shuwen.gcdj.dto.jwt.JWTVerifyDto;
import com.shuwen.gcdj.dto.power.SearchPowerApiDto;
import com.shuwen.gcdj.dto.role.SearchRolePowerDto;
import com.shuwen.gcdj.dto.sso.*;
import com.shuwen.gcdj.entity.*;
import com.shuwen.gcdj.exception.IErrorCodeFactory;
import com.shuwen.gcdj.mapper.mysql.AccountMapper;
import com.shuwen.gcdj.mapper.mysql.AccountRoleMapper;
import com.shuwen.gcdj.mapper.mysql.PowerApiMapper;
import com.shuwen.gcdj.mapper.mysql.PowerAuthDeptMapper;
import com.shuwen.gcdj.service.BaseService;
import com.shuwen.gcdj.service.api.ApiService;
import com.shuwen.gcdj.service.auth.SsoService;
import com.shuwen.gcdj.service.dept.DepartmentService;
import com.shuwen.gcdj.service.power.PowerApiService;
import com.shuwen.gcdj.service.power.PowerAuthService;
import com.shuwen.gcdj.service.power.PowerService;
import com.shuwen.gcdj.service.redis.RedisService;
import com.shuwen.gcdj.service.role.RolePowerService;
import com.shuwen.gcdj.service.role.RoleService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
@Slf4j
public class AccountService extends BaseService<AccountMapper, Account> {

    private final DepartmentService departmentService;
    private final AccountRoleService accountRoleService;
    private final RedisService redisService;
    private final PowerAuthService powerAuthService;
    private final ApiService apiService;
    private final PowerApiService powerApiService;
    private final RoleService roleService;
    private final RolePowerService rolePowerService;
    private final AddSysLogUtil addSysLogUtil;
    private final SsoService ssoService;
    private final PowerService powerService;

    private final AccountMapper accountMapper;
    private final AccountRoleMapper accountRoleMapper;
    private final PowerApiMapper powerApiMapper;
    private final PowerAuthDeptMapper powerAuthDeptMapper;

    @Autowired
    private final LdapTemplate ldapTemplate;

    /**
     * 添加自定义用户
     *
     * @param req         UserAddReq
     * @param initUserDto InitUserDto
     * @return CommonResult<AddResp>
     */
    public CommonResult<AddResp> add(UserAddReq req, InitUserDto initUserDto) {
        // 校验添加参数
        ErrorCode checkError = checkAdd(req);
        if (ErrorReport.hasError(checkError)) {
            return CommonResult.failed(checkError);
        }

        // 校验用户是否存在
        Account existAccount = getByLoginName(req.getLoginName());
        if (existAccount != null) {
            log.error("error: AccountService.add(req = {}, init = {}) 用户已存在，不能添加", req, initUserDto);
            return ErrorReport.fail(ErrorCode.UNIQUE_VALID_ERROR);
        }

        // 校验密码
        String originPwd = req.getPwd();
        String salt = createSalt();
        String inputSecretPwd = signSecretPwd(req.getLoginName(), originPwd, salt);
        Account account = new Account();
        account.setIsActive(req.getIsActive());
        BeanUtils.copyProperties(req, account);
        account.setSource(MemberCst.ACCOUNT_FROM_CUSTOMER_DEFINE);
        account.setPwd(inputSecretPwd);
        account.setSalt(salt);

        addExtInfo(account, initUserDto);
        if (save(account)) {
            AddResp resp = new AddResp();
            resp.setId(account.getId());
            //用户绑定角色
            UserBindRolesReq userBindRolesReq = new UserBindRolesReq();
            userBindRolesReq.setUserId(account.getId());
            //类型转换（后续绑定多个角色可去掉）
            List<String> objects = new ArrayList<>();
            objects.add(req.getRoleIds());

            userBindRolesReq.setRoleIds(objects);
            bindRoles(userBindRolesReq, initUserDto);
            //用户绑定部门
            UserBindDeptReq userBindDeptReq = new UserBindDeptReq();
            userBindDeptReq.setAccountId(account.getId());
            userBindDeptReq.setDeptId(req.getDeptId());
            bindDept(userBindDeptReq,initUserDto);
            return CommonResult.success(resp);
        }

        return CommonResult.failed(ErrorCode.FAILED);
    }

    private void addExtInfo(Account account, InitUserDto initUserDto) {
        if (initUserDto != null && initUserDto.getUserId() != null) {
            account.setCreateId(initUserDto.getUserId());
            account.setUpdateId(initUserDto.getUserId());
        }
    }

    private ErrorCode checkAdd(UserAddReq req) {
        if (req == null) {
            log.error("error: AccountService.add(req = null) params invalid");
            return ErrorCode.VALIDATE_FAILED;
        }

        if (req.getLoginName() == null || req.getLoginName().isEmpty()) {
            log.error("error: AccountService.add(req = {}) login_name invalid", req);
            return ErrorCode.VALIDATE_FAILED;
        }

        if (req.getShowName() == null || req.getShowName().isEmpty()) {
            req.setShowName(req.getLoginName());
        }

        if (req.getPwd() == null || req.getPwd().isEmpty()) {
            log.error("error: AccountService.add(req = {}) login_name invalid", req);
            return ErrorCode.VALIDATE_FAILED;
        }

        return ErrorCode.SUCCESS;
    }

    /**
     * 用户绑定多角色
     *
     * @param req         UserBindRolesReq
     * @param initUserDto InitUserDto
     * @return CommonResult<VoidResp>
     */
    public CommonResult<VoidResp> bindRoles(UserBindRolesReq req, InitUserDto initUserDto) {
        ErrorCode ck = checkBindRolesParams(req);
        if (ErrorReport.hasError(ck)) {
            return CommonResult.failed(ck);
        }

        // 超管账号不允许修改
        if (accountRoleService.isSuperUser(req.getUserId())) {
            throw new ApiException(IErrorCodeFactory.create(ErrorCode.INVALID_OP.getCode(), "超管账号角色不允许修改"));
        }

        QueryWrapper<AccountRole> qr = new QueryWrapper<>();
        qr.eq("account_id", req.getUserId());
        List<AccountRole> rows = accountRoleMapper.selectList(qr);
        if (rows != null && !rows.isEmpty()) {
            List<String> ids = new ArrayList<>();
            for (AccountRole row : rows) {
                ids.add(row.getId());
            }
            accountRoleMapper.deleteBatchIds(ids);
        }

        List<AccountRole> cs = new ArrayList<>();
        for (String roleId : req.getRoleIds()) {
            if (roleId == null || roleId.isEmpty()) {
                continue;
            }
            AccountRole g = new AccountRole();
            g.setAccountId(req.getUserId());
            g.setRoleId(roleId);
            if (initUserDto != null && initUserDto.getUserId() != null) {
                g.setCreateId(initUserDto.getUserId());
                g.setUpdateId(initUserDto.getUserId());
            }
            cs.add(g);
        }

        // 清空用户角色缓存 + 权限缓存
        redisService.deleteKeysWithPattern(AccountUtil.buildUserPowersKey("*"));
        redisService.deleteKeysWithPattern(AccountUtil.buildUserRolesKey("*"));

        if (!cs.isEmpty()) {
            return accountRoleService.saveMulti(cs);
        }

        return CommonResult.failed(ErrorCode.FAILED);
    }

    private ErrorCode checkBindRolesParams(UserBindRolesReq req) {
        if (req == null) {
            log.error("error: AccountService.checkBindRolesParams() req is null");
            return ErrorCode.VALIDATE_FAILED;
        }

        if (req.getUserId() == null || req.getUserId().isEmpty()) {
            log.error("error: AccountService.checkBindRolesParams(req = {}) userId is null", req);
            return ErrorCode.VALIDATE_FAILED;
        }

        if (req.getRoleIds() == null || req.getRoleIds().isEmpty()) {
            log.error("error: AccountService.checkBindRolesParams(req = {}) roleIds is null", req);
            return ErrorCode.VALIDATE_FAILED;
        }

        return ErrorCode.SUCCESS;
    }

    /**
     * 天河oss系统对接
     *
     * @param code String
     * @return CommonResult<LoginResp>
     */
    public CommonResult<LoginResp> ssoLogin(String code) {
        SsoAccessTokenResp accessTokenResp = ssoService.getAccessToken(code);
        if (accessTokenResp == null) {
            return CommonResult.failed(ErrorCode.OSS_ACCESS_TOKEN_ERROR);
        }

        // 获取oss登录用户信息
        SsoUserResp userResp = ssoService.getUserInfo(accessTokenResp.getAccessToken());
        if (userResp == null) {
            log.error("AccountService.ossLogin(code = {}) ssoService.getUserInfo(accessToken = {}) error.", code, accessTokenResp.getAccessToken());
            return CommonResult.failed(ErrorCode.OSS_GET_USER_INFO_ERROR);
        }

        Account account = getByLoginName(userResp.getAccountNo());
        if (account == null) {
            log.info("AccountService.ossLogin(code = {}) sso User not exist. error: ssoUser = {}", code, userResp);
            account = addOssAccount(userResp);
            if (account == null || account.getId().equals("")) {
                log.error("AccountService.ossLogin(code = {}) addOssAccount(userResp = {}) error.", code, userResp);
                return CommonResult.failed(ErrorCode.INVALID_OP);
            }
        }

        return CommonResult.success(doLogin(account, accessTokenResp));
    }

    /**
     * 退出登录
     *
     * @param initUserDto InitUserDto
     * @return CommonResult<VoidResp>
     */
    public CommonResult<VoidResp> logout(InitUserDto initUserDto) {
        if (initUserDto == null || initUserDto.getUserId() == null || initUserDto.getUserId().equals("")) {
            return CommonResult.success(new VoidResp());
        }

        CommonResult<UserLoginInfoCacheResp> cacheInfo = getCacheUserInfo(initUserDto.getUserId());
        if (cacheInfo == null) {
            log.error("step-1: AccountService.logout(userInfo = {}) error: getUserCache error: cacheInfo = {}", initUserDto, cacheInfo);
            return CommonResult.success(new VoidResp());
        }

        if (cacheInfo.getCode() != ErrorCode.SUCCESS.getCode()) {
            log.error("step-2: AccountService.logout(userInfo = {}) error: getUserCache error: cacheInfo = {}", initUserDto, cacheInfo);
            return CommonResult.failed(IErrorCodeFactory.create(ErrorCode.USER_LOGIN_CACHE_PARSE_ERROR.getCode(), "登录用户缓存异常"));
        }

        String accessToken = cacheInfo.getData().getOssAccessToken();
        if (accessToken != null && !accessToken.equals("")) {
            SsoNullResp resp = ssoService.logout(accessToken);
            if (resp == null) {
                log.error("AccountService.logout(userInfo = {}) ssoService.logout(accessToken = {}) error", initUserDto, accessToken);
            }
            // sso accessToken可能已经过期
            redisService.delete(AccountUtil.buildRedisUserInfoKey(initUserDto.getUserId()));
            return CommonResult.success(new VoidResp());
        }
        redisService.delete(AccountUtil.buildRedisUserInfoKey(initUserDto.getUserId()));
        MemberCst.LOGIN_USER_ID="Id";
        return CommonResult.success(new VoidResp());
    }

    /**
     * 账户+密码登录 (改造2022-03-08: 仅支持自定义账号登录)
     *
     * @param req LoginReq
     * @return LoginResp
     */
    public CommonResult<LoginResp> login(LoginReq req) {
        log.info("AccountService.login() origin req = {}", req);

        // 检查登录参数
        ErrorCode checkLogin = checkLoginParams(req);
        if (ErrorReport.hasError(checkLogin)) {
            return ErrorReport.fail(checkLogin);
        }
        log.info("AccountService.login() check param success!");

        // check pwd
        LoginReqDto loginReqDto = new LoginReqDto();
        loginReqDto.setFrom(Constants.USER_LOGIN_FROM);
        loginReqDto.setIsSecret(Constants.IS_SECRET);
        BeanUtils.copyProperties(req, loginReqDto);
        log.info(String.valueOf(req));
        //
        ErrorCode validPwd=validSecretPwd(loginReqDto);

        if (ErrorReport.hasError(validPwd)) {
            return CommonResult.failed(validPwd);
        }
        log.info("AccountService.login() 前端密码反解析 success!");

        // check user exist
        Account account = getByLoginName(loginReqDto.getLoginName());
        if (account == null) {
            return CommonResult.failed(ErrorCode.USER_NOT_ERROR);
        }

        // 验证用户登录账户和密码
        ErrorCode validLoginNamePwd=validLoginNameAndPwd(account, loginReqDto);
        if(validLoginNamePwd.getCode()!=200){
            return CommonResult.failed(validLoginNamePwd);
        }
        log.info("AccountService.login() 检验密码 success!");
        return CommonResult.success(doLogin(account, null));
    }

    /**
     * 动态刷新用户缓存信息，不修改token
     *
     * @param userId String
     * @return CommonResult<LoginResp>
     */
    public CommonResult<LoginResp> refresh(String userId, String jwtToken) {
        LoginResp resp = new LoginResp();
        boolean isSupper = accountRoleService.isSuperUser(userId);
        resp.setIsSuper(isSupper ? 1 : 0);
        resp.setToken(jwtToken);

        String key = AccountUtil.buildRedisUserInfoKey(userId);
        String userCacheStr = redisService.getString(key);
        if (userCacheStr == null || userCacheStr.isEmpty()) {
            log.error("error: AccountService.refresh(userId = {}) token已经过期", userId);
            return CommonResult.failed(ErrorCode.UNAUTHORIZED);
        }
        UserLoginInfoCacheResp cacheResp = JSON.toJavaObject(SerializeUtils.unSerialize(userCacheStr), UserLoginInfoCacheResp.class);
        if (cacheResp == null) {
            log.error("error: AccountService.refresh(userId = {}) 缓存数据反序列化异常，跳转用户登录页面", userId);
            return CommonResult.failed(ErrorCode.UNAUTHORIZED);
        }

        // 刷新sso access_token和校验sso_token
        boolean checkAndRefresh = validateAccessTokenAndRefresh(cacheResp, true);
        if (!checkAndRefresh) {
            return CommonResult.failed(ErrorCode.UNAUTHORIZED);
        }

        // set resp info
        BeanUtils.copyProperties(cacheResp, resp);

        // build roles
        List<RoleItemResp> roles = accountRoleService.rebuildRolesCacheByUserId(userId, isSupper);
        resp.setRoles(roles);

        // build powers
        Set<String> roleIds = new HashSet<>();
        for (RoleItemResp vv : roles) {
            roleIds.add(vv.getId());
        }
        List<PowerItemResp> power = rebuildPowersCacheByRoleIds(userId, Lists.newArrayList(roleIds), isSupper);
//        List<PowerItemResp> Menus = power.stream().filter(p -> p.getIsMenu().equals(MemberCst.IS_MENUS_YES)  ).collect(Collectors.toList());
        List<PowerItemResp> Powers =power.stream().filter(p -> p.getIsMenu().equals(MemberCst.IS_MENUS_NO) ).collect(Collectors.toList());
        List<MenuTreeResp> Menus = new ArrayList<>();
        Menus = powerService.getMenuTreeByRoleIds(new ArrayList<>(roleIds));
        PowersAndMenus powersAndMenus = new PowersAndMenus(Powers,Menus);
        resp.setPowers(powersAndMenus);

        log.info("AccountService.refresh(userId = {}) 刷新用户数据：{}", userId, resp);
        return CommonResult.success(resp);
    }

    /**
     * 验证sso access_token和刷新access_token
     * 并不一定会去验证access_token (允许5分钟延迟)
     * 并不一定会去刷新access_token (access_token过期前10分钟才会触发刷新动作)
     *
     * @param cacheResp        UserLoginInfoCacheResp
     * @param realTimeValidate boolean true:实时刷新access_token false:允许延迟
     * @return boolean
     */
    private boolean validateAccessTokenAndRefresh(UserLoginInfoCacheResp cacheResp, boolean realTimeValidate) {
        if (cacheResp == null) {
            return false;
        }

        if (cacheResp.getOssAccessToken() != null && !cacheResp.getOssAccessToken().equals("")) {
            // 校验access_token是否已过期
            if (cacheResp.getExpireAt() != null && cacheResp.getExpireAt() < TimeUtil.getCurrentSecondTime()) {
                log.info("AccountService.validateAccessTokenAndRefresh(cacheResp = {}), access_token时间过期, 当前时间 cst = {}", cacheResp, TimeUtil.getCurrentSecondTime());
                return false;
            }

            if (realTimeValidate) {
                SsoValidResp ssoValidResp = ssoService.validAccessToken(cacheResp.getOssAccessToken());
                if (ssoValidResp == null) {
                    log.info("AccountService.validateAccessTokenAndRefresh(cacheResp = {}), ssoService.validAccessToken(at = {}) 实时刷新: access_token验证失败, 当前时间 cst = {}", cacheResp, cacheResp.getOssAccessToken(), TimeUtil.getCurrentSecondTime());
                    return false;
                }

                // 重置校验access_token时间
                cacheResp.setValidateAccessTokenTimeAt(TimeUtil.getCurrentSecondTime());
            } else {
                // 每隔5分钟校验一次token - 异步刷新
                if (cacheResp.getValidateAccessTokenTimeAt() != null && (TimeUtil.getCurrentSecondTime() - cacheResp.getValidateAccessTokenTimeAt()) >= 300) {
                    SsoValidResp ssoValidResp = ssoService.validAccessToken(cacheResp.getOssAccessToken());
                    if (ssoValidResp == null) {
                        log.info("AccountService.validateAccessTokenAndRefresh(cacheResp = {}), ssoService.validAccessToken(at = {}) 异步刷新: access_token验证失败, 当前时间 cst = {}", cacheResp, cacheResp.getOssAccessToken(), TimeUtil.getCurrentSecondTime());
                        return false;
                    }

                    // 重置校验access_token时间
                    cacheResp.setValidateAccessTokenTimeAt(TimeUtil.getCurrentSecondTime());
                }
            }

            // 刷新access_token有效期 - 提前10分钟刷新
            if (cacheResp.getExpireAt() != null && TimeUtil.getCurrentSecondTime() < cacheResp.getExpireAt() && ((cacheResp.getExpireAt() - TimeUtil.getCurrentSecondTime()) < 600)) {
                SsoRefreshAccessTokenResp ssoRefreshAccessTokenResp = ssoService.refreshAccessToken(cacheResp.getOssAccessToken());
                if (ssoRefreshAccessTokenResp == null) {
                    log.error("error: AccountService.validateAccessTokenAndRefresh(cacheResp = {}) 刷新token错误，但当前access_token任然有效", cacheResp);
                    String saveCache = redisService.setString(AccountUtil.buildRedisUserInfoKey(cacheResp.getUserId()), SerializeUtils.serialize(cacheResp), MemberCst.ACCOUNT_ROLE_POWER_EXPIRE_SECONDS);
                    if (saveCache == null || saveCache.isEmpty()) {
                        log.error("AccountService.validateAccessTokenAndRefresh(cache = {}) redisService.setString(key = {}, value = {}) error", cacheResp, AccountUtil.buildRedisUserInfoKey(cacheResp.getUserId()), cacheResp);
                    }
                    return true;
                }

                if (ssoRefreshAccessTokenResp.getExpiresIn() != null && ssoRefreshAccessTokenResp.getExpiresIn() > 0) {
                    cacheResp.setExpireAt(TimeUtil.getCurrentSecondTime() + ssoRefreshAccessTokenResp.getExpiresIn() - 10);
                }
                // 注销refresh_token 只能使用一次
                cacheResp.setOssRefreshAccessToken("");
                cacheResp.setValidateAccessTokenTimeAt(TimeUtil.getCurrentSecondTime());
            }

            // 重置了用户中心缓存过期时间
            String saveCache = redisService.setString(AccountUtil.buildRedisUserInfoKey(cacheResp.getUserId()), SerializeUtils.serialize(cacheResp), MemberCst.ACCOUNT_ROLE_POWER_EXPIRE_SECONDS);
            if (saveCache == null || saveCache.isEmpty()) {
                log.error("AccountService.validateAccessTokenAndRefresh(cache = {}) redisService.setString(key = {}, value = {}) error", cacheResp, AccountUtil.buildRedisUserInfoKey(cacheResp.getUserId()), cacheResp);
            }
        }

        return true;
    }

    private Account addOssAccount(SsoUserResp userResp) {
        if (userResp == null) {
            return null;
        }
        Account account = new Account();
        Optional.ofNullable(userResp.getEmail()).ifPresent(account::setEmail);
        Optional.ofNullable(userResp.getUserId()).ifPresent(account::setId);
        Optional.ofNullable(userResp.getUsername()).ifPresent(account::setShowName);
        Optional.ofNullable(userResp.getAccountNo()).ifPresent(account::setLoginName);
        Optional.ofNullable(userResp.getDeptId()).ifPresent(account::setDeptId);
        Optional.ofNullable(userResp.getPhoneNo()).ifPresent(account::setMobile);
        account.setSource(MemberCst.ACCOUNT_FROM_OA);
        boolean ins = save(account);
        if (ins) {
            return account;
        }

        return null;
    }

    private LoginResp doLogin(Account account, SsoAccessTokenResp accessTokenResp) {
        LoginResp resp = new LoginResp();
        BeanUtils.copyProperties(account, resp);

        // jwt token
        String randomCode = UUID.randomUUID().toString();
        String jwtToken = JWTUtil.sign(account.getId(), account.getDeptId(), randomCode, MemberCst.JWT_TOKEN_SECRET_KEY);
        resp.setToken(jwtToken);
        resp.setUserId(account.getId());
        resp.setDeptName(departmentService.getDeptNameWithDefault(account.getDeptId(), "--"));

        boolean isSuper = accountRoleService.isSuperUser(account.getId());
        log.info("AccountService.doLogin(account = {}) isSuperUser(userId = {}) isSuper = {}", account, account.getId(), isSuper);
        resp.setIsSuper(isSuper ? 1 : 0);

        // build roles
        List<RoleItemResp> roles = accountRoleService.rebuildRolesCacheByUserId(account.getId(), isSuper);
        resp.setRoles(roles);

        // build powers
        List<String> roleIds = new ArrayList<>();
        for (RoleItemResp vv : roles) {
            roleIds.add(vv.getId());
        }
        List<PowerItemResp> power = rebuildPowersCacheByRoleIds(account.getId(), roleIds, isSuper);
//        List<PowerItemResp> Menus = power.stream().filter(p -> p.getIsMenu().equals(MemberCst.IS_MENUS_YES)).collect(Collectors.toList());
        List<PowerItemResp> Powers =power.stream().filter(p -> p.getIsMenu().equals(MemberCst.IS_MENUS_NO)).collect(Collectors.toList());

        //根据角色获取菜单
        List<MenuTreeResp> Menus = new ArrayList<>();
        Menus = powerService.getMenuTreeByRoleIds(new ArrayList<>(roleIds));

        PowersAndMenus powersAndMenus = new PowersAndMenus(Powers,Menus);
        resp.setPowers(powersAndMenus);

        // 序列化存入redis
        UserLoginInfoCacheResp cacheResp = new UserLoginInfoCacheResp();
        if (accessTokenResp != null && account.getSource() != null && account.getSource().equals(MemberCst.ACCOUNT_FROM_OA)) {
            cacheResp.setOssAccessToken(accessTokenResp.getAccessToken());
            cacheResp.setOssRefreshAccessToken(accessTokenResp.getRefreshToken());
            // -10 考虑本次请求时间延迟
            cacheResp.setExpireAt(TimeUtil.getCurrentSecondTime() + accessTokenResp.getExpiresIn() - 10);
            // 登录设置验证token时间戳-秒
            cacheResp.setValidateAccessTokenTimeAt(TimeUtil.getCurrentSecondTime());
        }
        BeanUtils.copyProperties(resp, cacheResp);
        cacheResp.setRandomCode(randomCode);
        String saveCache = redisService.setString(AccountUtil.buildRedisUserInfoKey(resp.getUserId()), SerializeUtils.serialize(cacheResp), MemberCst.ACCOUNT_ROLE_POWER_EXPIRE_SECONDS);
        if (saveCache == null || saveCache.isEmpty()) {
            log.error("AccountService.doLogin() redisService.setString(key = {}, value = {}) error", AccountUtil.buildRedisUserInfoKey(resp.getUserId()), cacheResp);
        }
        //上一次登录日期
        List<Log> loginLog = addSysLogUtil.list(new LambdaQueryWrapper<Log>()
                .eq(Log::getType,4)
                .eq(Log::getModule,0)
                .orderByDesc(Log::getCreateTime)
        );
        if (CollectionUtils.isEmpty(loginLog)) {
            resp.setLastLoginDateTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        } else {
            resp.setLastLoginDateTime(loginLog.get(0).getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }


        log.info("AccountService.doLogin(account = {}) successfully! result = {}", account, resp);
        return resp;
    }

    /**
     * 优先从缓存中获取用户角色信息
     *
     * @param userId  String
     * @param isSuper boolean
     * @return List<RoleItemResp>
     */
    private List<RoleItemResp> getRolesByUserIdFromCache(String userId, boolean isSuper) {
        String key = AccountUtil.buildUserRolesKey(userId);
        String val = redisService.getString(key);

        if (val == null || val.isEmpty()) {
            return accountRoleService.rebuildRolesCacheByUserId(userId, isSuper);
        }

        SuperRoleCacheResp cacheResp = JSON.toJavaObject(SerializeUtils.unSerialize(val), SuperRoleCacheResp.class);
        if (cacheResp == null || cacheResp.getItems() == null) {
            return new ArrayList<>();
        }

        return cacheResp.getItems();
    }

    /**
     * 优先从缓存获取权限点信息
     *
     * @param roleIds List<String>
     * @param isSuper boolean
     * @return List<PowerItemResp>
     */
    private List<PowerItemResp> rebuildPowersCacheByRoleIds(String userId, List<String> roleIds, boolean isSuper) {
        String key = AccountUtil.buildUserPowersKey(userId);
        List<PowerItemResp> resp;
        if (isSuper) {
            resp = getSuperPowers();
        } else {
            resp = getNormalPowersByRoleIds(roleIds);
        }

        if (!resp.isEmpty()) {
            SuperPowerCacheResp cacheResp = new SuperPowerCacheResp();
            cacheResp.setItems(resp);
            redisService.setString(key, SerializeUtils.serialize(cacheResp), MemberCst.ACCOUNT_USER_POWERS_EXPIRE_SECONDS);
            return resp;
        }

        return new ArrayList<>();
    }

    /**
     * 优先从缓存获取权限点信息
     *
     * @param roleIds List<String>
     * @param isSuper boolean
     * @return List<PowerItemResp>
     */
    private List<PowerItemResp> getPowersByRoleIdsFromCache(String userId, List<String> roleIds, boolean isSuper) {
        String key = AccountUtil.buildUserPowersKey(userId);
        String val = redisService.getString(key);

        if (val == null || val.isEmpty()) {
            return rebuildPowersCacheByRoleIds(userId, roleIds, isSuper);
        }

        SuperPowerCacheResp cacheResp = JSON.toJavaObject(SerializeUtils.unSerialize(val), SuperPowerCacheResp.class);
        if (cacheResp == null || cacheResp.getItems() == null) {
            return new ArrayList<>();
        }

        return cacheResp.getItems();
    }

    /**
     * 获取超级管理员权限点 - 从DB获取
     */
    private List<PowerItemResp> getSuperPowers() {
        List<PowerItemResp> search = accountMapper.getPowersByUserId(null, 1);
        if (search == null) {
            return new ArrayList<>();
        }
        return search;
    }

    /**
     * 获取普通管理员权限点 - 从DB获取
     */
    private List<PowerItemResp> getNormalPowersByRoleIds(List<String> roleIds) {
        Set<String> powerCodes = getPowerCodesByRoleIds(roleIds);
        List<PowerItemResp> resp = new ArrayList<>();
        if (powerCodes.isEmpty()) {
            return resp;
        }

        resp = accountMapper.getPowersByPowerCodes(Lists.newArrayList(powerCodes));
        return resp;
    }

    private Set<String> getPowerCodesByRoleIds(List<String> roleIds) {
        // 获取角色下的所有权限点
        if (roleIds == null || roleIds.isEmpty()) {
            return new HashSet<>();
        }

        // 获取当前角色及子角色
        List<Role> roles = roleService.searchCurrentAndSubRoles(roleIds);
        if (roles == null || roles.isEmpty()) {
            return new HashSet<>();
        }
        List<String> searchRoleIds = new ArrayList<>();
        Map<String, Boolean> maps = new HashMap<>();
        for (Role role : roles) {
            if (!maps.containsKey(role.getId())) {
                maps.put(role.getId(), true);
                searchRoleIds.add(role.getId());
            }
        }

        if (searchRoleIds.isEmpty()) {
            return new HashSet<>();
        }

        SearchRolePowerDto searchRolePowerDto = new SearchRolePowerDto();
        searchRolePowerDto.setIsDelete(Lists.newArrayList(Constants.IS_DELETE_NORMAL));
        searchRolePowerDto.setRoleIds(roleIds);
        List<RolePower> rolePowers = rolePowerService.searchList(searchRolePowerDto);
        if (rolePowers == null) {
            return new HashSet<>();
        }

        Set<String> powerCodes = new HashSet<>();
        for (RolePower rolePower : rolePowers) {
            if (rolePower.getPowerCode() != null && !rolePower.getPowerCode().isEmpty()) {
                powerCodes.add(rolePower.getPowerCode());
            }
        }

        return powerCodes;
    }

    /**
     * 检查登录参数
     *
     * @param req LoginReq
     * @return ErrorCode
     */
    private ErrorCode checkLoginParams(LoginReq req) {


        if (req.getLoginName() == null || req.getLoginName().isEmpty()) {
            log.error("AccountService.checkLoginParams(req = {}) check params.loginName error", req);
            return ErrorCode.VALIDATE_FAILED;
        }

        if (req.getPwd() == null || req.getPwd().isEmpty()) {
            log.error("AccountService.checkLoginParams(req = {}) check params.pwd error", req);
            return ErrorCode.VALIDATE_FAILED;
        }

        return ErrorCode.SUCCESS;
    }

    /**
     * 账号启用/停用
     *
     * @param req         UserActiveReq
     * @param initUserDto InitUserDto
     * @return CommonResult<VoidResp>
     */
    public CommonResult<VoidResp> activeSetting(UserActiveReq req, InitUserDto initUserDto) {

        InitUserDto init=new InitUserDto();
        if (StringUtils.isEmpty(req)) {
            return ErrorReport.fail(ErrorCode.VALIDATE_FAILED);
        }

        if (StringUtils.isEmpty(req.getUserId())) {
            return ErrorReport.fail(ErrorCode.VALIDATE_FAILED);
        }

        if (StringUtils.isEmpty(req.getIsActive())) {
            return ErrorReport.fail(ErrorCode.VALIDATE_FAILED);
        }

        Account account = getById(req.getUserId());
        if (StringUtils.isEmpty(account)) {
            return ErrorReport.fail(ErrorCode.USER_NOT_EXIST);
        }


        account.setIsActive(req.getIsActive());
        addExtInfo(account, initUserDto);
        boolean outcome= updateById(account);
        init.setUserId(req.getUserId());
        if(outcome){
            logout(init);
        }
        addSysLogUtil.addLog(initUserDto, LogTypeEnum.LOG_TYPE_UPDATE.getTypeNum(), LogModuleEnum.LOG_MODULE_USER.getModuleNum(),"启用了"+req.getUserId(),req);
        return ErrorReport.success(new VoidResp());
    }

    /**
     * 加密密码校验
     *
     * @param loginReqDto LoginReqDto
     * @return ErrorCode
     */
    private ErrorCode validSecretPwd(LoginReqDto loginReqDto) {
        if (loginReqDto.getIsSecret().equals(MemberCst.IS_SECRET_NO)) {
            log.info("AccountService.validSecretPwd({}) need not check pwd", loginReqDto);
            return ErrorCode.SUCCESS;
        }

        try {
            String originPwd = unpackSecretPwd(loginReqDto.getPwd());
            loginReqDto.setDecodePwd(originPwd);
            return ErrorCode.SUCCESS;
        } catch (Exception e) {
            log.error("error: AccountService.validSecretPwd({}) 密码解密异常", loginReqDto);
            return ErrorCode.USER_PWD_UNPACK_INVALID;
        }
    }

    /**
     * 解密加密后的密码 secretPwd
     * <p>
     * 如：用户输入的密码为：abcd123456
     * 传输的密码格式为： base64("iBs"+base64("abcd123456"+"eCe"))
     * 最终传输的密码为：aUJzWVdKalpERXlNelExTm1WRFpRPT0=
     *
     * @param secretPwd String
     * @return String
     */
    private String unpackSecretPwd(String secretPwd) {
        log.info("AccountService.(secretPwd = {})", secretPwd);
        byte[] dec = Base64.getDecoder().decode(secretPwd);
        String decodePwd = new String(dec);
        log.info("AccountService.unpackSecretPwd(secretPwd = {}) decodePwd = {}", secretPwd, decodePwd);
        if (decodePwd.isEmpty()) {
            log.error("AccountService.getOriginPwdFromSecretPwd(secretPwd = {}) Base64.getDecoder().decode result is empty {}", secretPwd, decodePwd);
            throw new ApiException(ErrorCode.VALIDATE_FAILED);
        }

        // 加密密码反解
        Pattern pattern = Pattern.compile(MemberCst.PWD_PREFIX + "(.*)");
        Matcher matcherPrefix = pattern.matcher(decodePwd);
        if (!matcherPrefix.find()) {
            log.error("AccountService.unpackSecretPwd(decodePwd = {}) 密码前缀不符合加密规则", decodePwd);
            throw new ApiException(ErrorCode.VALIDATE_FAILED);
        }

        dec = Base64.getDecoder().decode(matcherPrefix.group(1));
        decodePwd = new String(dec);
        log.info("AccountService.unpackSecretPwd(secretPwd = {}) decodePwd = {} step-2-unpack", secretPwd, decodePwd);
        pattern = Pattern.compile("(.*)" + MemberCst.PWD_SUFFIX);
        Matcher matcherSuffix = pattern.matcher(decodePwd);
        if (!matcherSuffix.find()) {
            log.error("AccountService.getOriginPwdFromSecretPwd(decodePwd = {}) 密码后缀不符合加密规则", decodePwd);
            throw new ApiException(ErrorCode.VALIDATE_FAILED);
        }
        log.info("AccountService.unpackSecretPwd(secretPwd = {}) unpackPwd = {}", secretPwd, matcherSuffix.group(1));
        return matcherSuffix.group(1);
    }

    /**
     * 验证用户登录账户和密码
     *
     * @param account     Account
     * @param loginReqDto LoginReqDto
     * @return ErrorCode
     */
    private ErrorCode validLoginNameAndPwd(Account account, LoginReqDto loginReqDto) {
        if (account.getIsActive().equals(MemberCst.IS_ACTIVE_SYNC_ACCOUNT_ACTIVE)) {
            log.error("AccountService.validLoginNameAndPwd({}) account.isActive(account = {}) error", loginReqDto, account);
            return ErrorCode.USER_FORBIDDEN_LOGIN;
        }

        if (StringUtils.isEmpty(account.getSource())) {
            log.error("AccountService.validLoginNameAndPwd({}) from is null. account = {}", loginReqDto, account);
            return ErrorCode.INVALID_OP;
        }

        if (StringUtils.isEmpty(account.getPwd())) {
            log.error("AccountService.validLoginNameAndPwd({}), account = {}, 数据库账户密码不能为空，数据完整性异常", loginReqDto, account);
            return ErrorCode.USER_DATA_INVALID;
        }

        if(account.getIsActive() == 1)
        {
            return ErrorCode.USER_NOT_ERROR;
        }

        // 自定义账户验证
        if (account.getSource().equals(MemberCst.ACCOUNT_FROM_CUSTOMER_DEFINE)) {
            // 自定义账户
            String salt = account.getSalt();
            salt = salt == null ? "" : salt;
            String signPwd = signSecretPwd(loginReqDto.getLoginName(), loginReqDto.getPwd(), salt);
            if (!signPwd.equals(account.getPwd())) {
                log.error("AccountService.validLoginNameAndPwd({}), account = {}, 登录用户密码不匹配", loginReqDto, account);
                return ErrorCode.USER_ACCOUNT_PWD_UN_MATCH;
            }
            MemberCst.LOGIN_USER_ID=account.getId();
            if (MemberCst.ACCOUNT_LOGIN_TIME_INITIALIZED.equals(account.getLoginTimes())) {
                redisService.setString(account.getId(), SerializeUtils.serialize(account), MemberCst.ACCOUNT_USER_EXPIRE_SECONDS);
            }
            accountMapper.updateLoginTimes(account.getId());

            String accountInfo = redisService.getString(account.getId());
            if (StringUtils.isEmpty(accountInfo)){
                return ErrorCode.USER_NOT_EXIST;
            }
            return ErrorCode.SUCCESS;
        }

        log.error("AccountService.validLoginNameAndPwd({}), account = {} 暂不支持其他来源用户登录", loginReqDto, account);
        return ErrorCode.INVALID_OP;
    }

    /**
     * 加盐值
     *
     * @return String
     */
    private String createSalt() {
        return Calendar.getInstance().getTimeInMillis() + "";
    }

    /**
     * 加密密码签名
     *
     * @param loginName String
     * @param originPwd String
     * @param salt      String
     * @return String
     */
    private String signSecretPwd(String loginName, String originPwd, String salt) {
        return CryptTool.getMD5Str(loginName + originPwd + salt).toUpperCase();
    }

    public Account getByLoginName(String loginName) {
        return getOne(new QueryWrapper<Account>().lambda().eq(Account::getLoginName,loginName).in(Account::getIsDelete, Constants.IS_DELETE_NORMAL));
    }


    /**
     * 管理后台用户列表页接口
     *
     * @param req UserListReq
     * @return List<UserListItemResp>
     */
    public CommonResult<CommonPage<UserListWithoutAuthItemResp>> getBaseInfoList(UserListAdminReq req, InitUserDto initUserDto) {
        UserListReq searchReq = buildUserListSearchReq(req);
        return getBaseList(searchReq, initUserDto);
    }


    /**
     * 管理后台用户列表页接口分页
     *
     * @param req UserListReq
     * @return List<UserListItemResp>
     */
    public CommonResult<CommonPage<UserListItemResp>> getAdminListPage(UserListAdminReq req, InitUserDto initUserDto) {
        boolean isSuper = false;
        if (initUserDto != null && initUserDto.getUserId() != null && !initUserDto.getUserId().isEmpty()) {
            isSuper = accountRoleService.isSuperUser(initUserDto.getUserId());
        }
        return getList(buildUserListSearchReq(req), isSuper);
    }

    /**
     * 管理后台用户列表页接口分页
     *
     * @param req UserListReq
     * @return List<UserListItemResp>
     */
    public CommonResult<List<UserListItemResp>> getAdminList(UserListAdminReqNoPage req, InitUserDto initUserDto) {
        boolean isSuper = false;
        if (initUserDto != null && initUserDto.getUserId() != null && !initUserDto.getUserId().isEmpty()) {
            isSuper = accountRoleService.isSuperUser(initUserDto.getUserId());
        }
        return getNoPage(buildUserListNoPage(req), isSuper);
    }


    private UserListReq buildUserListSearchReq(UserListAdminReq req) {
        UserListReq searchReq = new UserListReq();
        Optional.ofNullable(req.getLoginName()).ifPresent(searchReq::setLoginName);
        Optional.ofNullable(req.getShowName()).ifPresent(searchReq::setShowName);
        Optional.ofNullable(req.getPageNum()).ifPresent(searchReq::setPageNum);
        Optional.ofNullable(req.getPageSize()).ifPresent(searchReq::setPageSize);
        if (!StringUtils.isEmpty(req.getIsActive())) {
            searchReq.setIsActive(req.getIsActive());
        }

        if (req.getSearchUserWithRole() != null && req.getSearchUserWithRole() == 1) {
            searchReq.setSearchUserWithRole(1);
        }

        // 设置角色id
        if (req.getRoleId() != null && !req.getRoleId().isEmpty()) {
            searchReq.setRoleId(req.getRoleId());
        }

        // 设置部门
        if (req.getDeptId() != null && !req.getDeptId().isEmpty()) {
            List<DeptListItemResp> res = departmentService.searchCurrentAndSubDept(Lists.newArrayList(req.getDeptId()));
            if (res == null || res.isEmpty()) {
                searchReq.setIds(Lists.newArrayList("-1"));
                return searchReq;
            }
            searchReq.setDeptIds(req.getDeptId());
        }
        return searchReq;
    }
    private UserListNoPage buildUserListNoPage(UserListAdminReqNoPage req) {
        UserListNoPage searchReq = new UserListNoPage();
        Optional.ofNullable(req.getLoginName()).ifPresent(searchReq::setLoginName);
        Optional.ofNullable(req.getShowName()).ifPresent(searchReq::setShowName);
        if (!StringUtils.isEmpty(req.getIsActive())) {
            searchReq.setIsActive(req.getIsActive());
        }

        if (req.getSearchUserWithRole() != null && req.getSearchUserWithRole() == 1) {
            searchReq.setSearchUserWithRole(1);
        }

        // 设置角色id
        if (req.getRoleId() != null && !req.getRoleId().isEmpty()) {
            searchReq.setRoleId(req.getRoleId());
        }

        // 设置部门
        if (req.getDeptId() != null && !req.getDeptId().isEmpty()) {
            List<DeptListItemResp> res = departmentService.searchCurrentAndSubDept(Lists.newArrayList(req.getDeptId()));
            if (StringUtils.isEmpty(res) || res.isEmpty()) {
                searchReq.setIds(Lists.newArrayList("-1"));
                return searchReq;
            }

            searchReq.setDeptIds(req.getDeptId());
        }
        return searchReq;
    }
    /**
     * 列表页接口
     *
     * @param req UserListReq
     * @return List<UserListItemResp>
     */
    public CommonResult<CommonPage<UserListItemResp>> getList(UserListReq req, boolean isSuper) {
        Page<UserListItemResp> page = new Page<>(req.getPageNum(), req.getPageSize(), true);
        IPage<UserListItemResp> data = accountMapper.getListPage(page, req, isSuper ? 1 : 0);
        List<UserListItemResp> res = data.getRecords();
        Page<UserListItemResp> resPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        resPage.setRecords(res);
        return CommonResult.success(CommonPage.restPage(resPage));
    }

    /**
     * 列表页接口部分页
     *
     * @param req UserListReq
     * @return List<UserListItemResp>
     */
    public CommonResult< List<UserListItemResp>> getNoPage(UserListNoPage req, boolean isSuper) {
        List<UserListItemResp> data = accountMapper.getList(req, isSuper ? 1 : 0);
        return CommonResult.success(data);
    }


    /**
     * 列表页接口
     *
     * @param req UserListReq
     * @return List<UserListItemResp>
     */
    public CommonResult<CommonPage<UserListWithoutAuthItemResp>> getBaseList(UserListReq req, InitUserDto initUserDto) {
        boolean isSuper = accountRoleService.isSuperUser(initUserDto.getUserId());
        Page<UserListWithoutAuthItemResp> page = new Page<>(req.getPageNum(), req.getPageSize(), true);
        IPage<UserListWithoutAuthItemResp> data = accountMapper.getUserBaseInfoList(page, req, isSuper ? 1 : 0);
        List<UserListWithoutAuthItemResp> res = data.getRecords();
        Page<UserListWithoutAuthItemResp> resPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        resPage.setRecords(res);
        return CommonResult.success(CommonPage.restPage(resPage));
    }

    /**
     * JWT token验证
     *
     * @param jwtToken String
     * @return ErrorCode
     */
    public JWTVerifyDto validJWTToken(String jwtToken) {
        JWTVerifyDto vo = JWTUtil.verify(jwtToken, MemberCst.JWT_TOKEN_SECRET_KEY);
        if (ErrorReport.hasError(vo.getErrorCode())) {
            log.error("AccountService.validJWTToken(jwtToken = {}) JWTUtil.verify() error: {}", jwtToken, vo.getErrorCode());
            return vo;
        }

        // 二次验证
        String s = redisService.getString(AccountUtil.buildRedisUserInfoKey(vo.getDecodeUserId()));
        if (StringUtils.isEmpty(s) || s.isEmpty()) {
            log.error("AccountService.validJWTToken(token = {}) redisService.getString(key = {}) error: cache is empty", jwtToken, AccountUtil.buildRedisUserInfoKey(vo.getDecodeUserId()));
            vo.setErrorCode(ErrorCode.TOKEN_EXPIRED);
            return vo;
        }

        UserLoginInfoCacheResp cacheResp = JSON.toJavaObject(SerializeUtils.unSerialize(s), UserLoginInfoCacheResp.class);
        if (StringUtils.isEmpty(cacheResp)) {
            log.error("AccountService.validJWTToken(token = {}) JSON.toJavaObject({}) error: cache decode error", jwtToken, s);
            vo.setErrorCode(ErrorCode.USER_LOGIN_CACHE_PARSE_ERROR);
            return vo;
        }

        // add - ldap账号、sso账号触发验证access_token及刷新access_token机制
        // 验证失败则跳转登录页
        boolean checkAndRefreshAccessToken = validateAccessTokenAndRefresh(cacheResp, false);
        if (!checkAndRefreshAccessToken) {
            log.error("AccountService.validJWTToken(token = {}) validateAccessTokenAndRefresh(cache = {}, false) error: 校验+刷新SsoAccessToken错误", jwtToken, cacheResp);
            vo.setErrorCode(ErrorCode.UNAUTHORIZED);
            return vo;
        }

        if (vo.getDecodeRandomCode() != null && cacheResp.getRandomCode() != null && vo.getDecodeRandomCode().equals(cacheResp.getRandomCode())) {
            log.info("AccountService.validJWTToken(token = {}) decode successfully! vo = {},  cache = {}", jwtToken, vo, cacheResp);
            vo.setErrorCode(ErrorCode.SUCCESS);
            return vo;
        }

        log.error("error: AccountService.validJWTToken(token = {}) randomCode not equal: vo = {}, cacheResp.getRandomCode() = {}", jwtToken, vo, cacheResp.getRandomCode());
        vo.setErrorCode(ErrorCode.TOKEN_EXPIRED);
        return vo;
    }

    /**
     * 验证Api接口访问控制权限
     *
     * @param userId  String
     * @param apiCode String
     * @return ErrorCode
     */
    public ErrorCode apiAccess(String userId, String apiCode) {

        if (StringUtils.isEmpty(apiCode) || apiCode.isEmpty()) {
            log.error("AccountService.apiAccess(userId = {}, apiCode= {}) error: apiCode不能为空", userId, apiCode);
            return ErrorCode.VALIDATE_FAILED;
        }

        boolean isSuper = accountRoleService.isSuperUser(userId);
        if (isSuper) {
            log.info("AccountService.apiAccess(userId = {}, apiCode= {}) 超级管理员无需鉴权!", userId, apiCode);
            return ErrorCode.SUCCESS;
        }

        // build powers
        List<RoleItemResp> roles = getRolesByUserIdFromCache(userId, false);
        List<String> roleIds = new ArrayList<>();
        for (RoleItemResp vv : roles) {
            roleIds.add(vv.getId());
        }
        //获取角色下的所有权限
        List<PowerItemResp> powers = getNormalPowersByRoleIds(roleIds);
        if (powers.isEmpty()) {
            log.error("AccountService.apiAccess(userId = {}, apiCode= {}) 无权访问【{}】", userId, apiCode, apiCode);
            return ErrorCode.FORBIDDEN;
        }

        List<String> powerCodes = new ArrayList<>();
        Map<String, Boolean> powerMaps = new HashMap<>();
        for (PowerItemResp power : powers) {
            powerCodes.add(power.getCode());
            powerMaps.put(power.getCode(), true);
        }


        //用户API鉴权
        Api apiInfo = apiService.getOneByCode(apiCode);
        if (apiInfo != null && apiInfo.getPowerCode() != null && !apiInfo.getPowerCode().isEmpty() && powerMaps.containsKey(apiInfo.getPowerCode())) {
            log.info("AccountService.apiAccess(userId = {}, apiCode= {}) 鉴权成功: [从Api侧鉴权]", userId, apiCode);
            return ErrorCode.SUCCESS;
        }

        log.error("AccountService.apiAccess(userId = {}, apiCode= {}) 暂无权限访问【{}】", userId, apiCode, apiCode);
        return ErrorCode.FORBIDDEN;
    }

    /**
     * 从redis获取用户缓存并验证
     *
     * @param userId String
     * @return CommonResult<UserLoginInfoCacheResp>
     */
    public CommonResult<UserLoginInfoCacheResp> getCacheUserInfo(String userId) {
        // 二次验证
        String s = redisService.getString(AccountUtil.buildRedisUserInfoKey(userId));
        if (StringUtils.isEmpty(s)) {
            log.error("AccountService.getCacheUserInfo(userId = {}) error: cache is empty", userId);
            return CommonResult.failed(ErrorCode.TOKEN_EXPIRED);
        }

        UserLoginInfoCacheResp cacheResp = JSON.toJavaObject(SerializeUtils.unSerialize(s), UserLoginInfoCacheResp.class);
        if (StringUtils.isEmpty(cacheResp)) {
            log.error("AccountService.getCacheUserInfo(userId = {}) JSON.toJavaObject({}) error: cache decode error", userId, s);
            return CommonResult.failed(ErrorCode.USER_LOGIN_CACHE_PARSE_ERROR);
        }

        return CommonResult.success(cacheResp);
    }

    /**
     * 获取用户详情
     *
     * @param userId String
     * @return CommonResult<UserDetailResp>
     */
    public CommonResult<UserDetailResp> getDetail(String userId) {
        if (StringUtils.isEmpty(userId)) {
            return CommonResult.failed(ErrorCode.VALIDATE_FAILED);
        }

        Account account = getById(userId);
        if (StringUtils.isEmpty(account)) {
            return CommonResult.failed(ErrorCode.USER_NOT_EXIST);
        }

        List<UserDetailResp> res = accountMapper.getMultiUserDetail(Lists.newArrayList(userId));
        if (CollectionUtils.isEmpty(res)) {
            return CommonResult.failed(ErrorCode.USER_NOT_EXIST);
        }

        UserDetailResp resp = res.get(0);
        boolean isSuper = accountRoleService.isSuperUser(userId);
        List<RoleItemResp> roles = getRolesByUserIdFromCache(userId, isSuper);
        Optional.ofNullable(roles).ifPresent(resp::setRoles);
        return CommonResult.success(resp);
    }


    public CommonResult<String> deleteUser(String userId,InitUserDto initUserDto){
        List<String> strings = FrpStringUtil.unGenerateInCondition(userId);
        for (String s : strings) {
            InitUserDto init=new InitUserDto();
            Account account = accountMapper.selectById(s);
            List<RoleItemResp> roles = accountMapper.getRoles(s, RolePowerCst.ROLE_CODE_SUPER);
            if (StringUtils.isEmpty(account)){
                throw new ApiException(ErrorCode.USER_NOT_EXIST);
            }
            if (!CollectionUtils.isEmpty(roles)){
                throw new ApiException(ErrorCode.INVALID_ERROR_DELETE_ADMIN);
            }
          int outcome=  accountMapper.update(account,new UpdateWrapper<Account>().lambda()
                    .eq(Account::getId,s)
                    .set(Account::getUpdateId,initUserDto.getUserId())
                    .set(Account::getIsDelete,1));
            init.setUserId(userId);
            if(outcome>0){
                logout(init);
            }

        }
        return CommonResult.success("");
    }
    /**
     * 修改用户信息
     *
     * @param req String
     * @return CommonResult<UserDetailResp>
     */
    public CommonResult<String> updateUser(UserUpdataReq req,InitUserDto initUserDto) {
        Account account = accountMapper.selectById(req.getId());
        BeanUtils.copyProperties(req,account);
        if (StringUtils.isEmpty(account)){
            return CommonResult.failed(ErrorCode.DATA_NOT_EXIST);
        }
        //修改用户绑定的角色
        UserBindRolesReq userBindRolesReq = new UserBindRolesReq();
        userBindRolesReq.setUserId(req.getId());
        List<String> RoleId = new ArrayList<>();
        RoleId.add(req.getRoleId());
        userBindRolesReq.setRoleIds(RoleId);
        bindRoles(userBindRolesReq,initUserDto);

        accountMapper.updateById(account);
        return CommonResult.success("");
    }
    /**
     * 批量获取权限点的域控范围<不传ApiCode>
     *
     * @param req UserDomainControlListMultiReq
     * @return CommonResult<Map < String, UserDomainControlItemResp>>
     */
    public CommonResult<Map<String, UserDomainControlItemResp>> getMultiDomainControl(UserDomainControlListMultiReq req) {
        ErrorCode ck = checkMultiDomainControl(req);
        if (ErrorReport.hasError(ck)) {
            return CommonResult.failed(ck);
        }

        Map<String, UserDomainControlItemResp> resp = new HashMap<>();
        for (String powerCode : req.getPowerCodes()) {
            UserDomainControlListReq bean = new UserDomainControlListReq();
            BeanUtils.copyProperties(req, bean);
            bean.setPowerCode(powerCode);
            CommonResult<UserDomainControlItemResp> single = getDomainControlWithoutApiCode(bean);
            if (single.getCode() != ErrorCode.SUCCESS.getCode()) {
                log.info("Info: user.getMultiDomainControl(req = {}) bean = {} 处理异常 error = {}", req, bean, single);
                continue;
            }
            resp.put(powerCode, single.getData());
        }

        return CommonResult.success(resp);
    }

    /**
     * 获取域控部门<不带ApiCode>
     *
     * @param req UserDomainControlReq
     * @return CommonResult<List < UserDomainControlItemResp>>
     */
    private CommonResult<UserDomainControlItemResp> getDomainControlWithoutApiCode(UserDomainControlListReq req) {
        req.setExcludeRoleCodes(RolePowerCst.getBizHiddenRoleCodes());
        ErrorCode checkParam = checkDomainControl(req, false);
        if (ErrorReport.hasError(checkParam)) {
            return CommonResult.failed(checkParam);
        }

        Account account = checkAndReturnAccount(req.getUserId());

        // 超级管理员不受域控
        boolean isSuper = accountRoleService.isSuperUser(account.getId());
        if (isSuper) {
            log.info("super: AccountService.getDomainControlWithoutApiCode(req = {}) 超管域控逻辑处理", req);
            return getSuperDomainControl(req);
        }

        // 普通管理员走role_power关系获取域控范围
        CommonResult<UserDomainControlItemResp> resp = withNormalRolePowerStep(req, req.getPowerCode(), false);
        log.info("domain info [getDomainControl]: AccountService.getDomainControlWithoutApiCode() withNormalRolePowerStep(req = {}, usePowerCode = {}), resp = {}", req, req.getPowerCode(), resp);
        return resp;
    }

    private Account checkAndReturnAccount(String userId) {
        Account account = getById(userId);
        if (StringUtils.isEmpty(account)) {
            log.error("error: AccountService.checkAndReturnAccount(userId = {}) user not found!", userId);
            throw new ApiException(ErrorCode.USER_NOT_EXIST);
        }

        return account;
    }


    /**
     * 获取域控部门
     *
     * @param req UserDomainControlReq
     * @return CommonResult<List < UserDomainControlItemResp>>
     */
    public CommonResult<UserDomainControlItemResp> getDomainControl(UserDomainControlListReq req) {
        req.setExcludeRoleCodes(RolePowerCst.getBizHiddenRoleCodes());
        ErrorCode checkParam = checkDomainControl(req, true);
        if (ErrorReport.hasError(checkParam)) {
            return CommonResult.failed(checkParam);
        }

        Account account = getById(req.getUserId());
        if (StringUtils.isEmpty(account)) {
            log.error("error: AccountService.getDomainControl(req = {}) user not found!", req);
            return CommonResult.failed(ErrorCode.USER_NOT_EXIST);
        }

        // 超级管理员不受域控
        boolean isSuper = accountRoleService.isSuperUser(req.getUserId());
        if (isSuper) {
            log.info("super: AccountService.getDomainControl(req = {}) 超管域控逻辑处理", req);
            return getSuperDomainControl(req);
        }

        // 前端无页面权限码，则走Api默认设置的权限码配置逻辑
        // step-0
        if (StringUtils.isEmpty(req.getPowerCode())) {
            log.info("AccountService.getDomainControl() searchPowerApi(req = {}) info: 前端没有传参数powerCode参数，走Api默认配置权限码逻辑", req);
            return withApiStepWrap(req);
        }

        // step-1 获取power-api直接配置
        PowerApi powerApi = searchPowerApi(req);
        String usePowerCode;
        if (StringUtils.isEmpty(powerApi)) {
            log.info("AccountService.getDomainControl() searchPowerApi(req = {}) 没有配置powerApi关系(powerCode = {}, apiCode = {}, userId = {})", req, req.getPowerCode(), req.getApiCode(), req.getUserId());
            return withApiStepWrap(req);
        }

        // step-2
        Integer settingType = powerApi.getSettingType();
        IErrorCode settingCheck = checkSettingType(settingType);
        if (ErrorReport.hasError(settingCheck)) {
            log.info("AccountService.getDomainControl() searchPowerApi(req = {}) powerApi绑定关系设置错误，走Api上默认权限码逻辑(powerApi = {})", req, powerApi);
            return withApiStepWrap(req);
        }

        if (settingType.equals(Constants.POWER_API_SETTING_TYPE_BY_DEFAULT)) {
            // 使用全局默认逻辑
            log.info("AccountService.getDomainControl(req = {}) 域控方式为: 全局统一默认逻辑", req);
            return CommonResult.success(withGlobalDefaultRolePower(req));
        }

        if (settingType.equals(Constants.POWER_API_SETTING_TYPE_BY_PAGE_CODE)) {
            // 信息范围和数据范围跟随前端传递的页面PowerCode
            log.info("AccountService.getDomainControl(req = {}) 域控方式为: 信息范围和数据范围跟随前端传递的页面PowerCode; usePowerCode = {}", req, req.getPowerCode());
            usePowerCode = req.getPowerCode();
        } else {
            // 信息范围和数据范围跟随Api配置的PowerCode
            log.info("begin: AccountService.getDomainControl(req = {}) 域控方式为: 信息范围和数据范围跟随Api配置的PowerCode", req);
            Api api = apiService.getOneByCode(req.getApiCode());
            if (api == null || api.getPowerCode() == null) {
                log.error("failed: domain info [2-1]: AccountService.getDomainControl(req = {}) apiService.getOneByCode(apiCode = {}) Api配置不存在", req, req.getApiCode());
                return CommonResult.failed(ErrorCode.DATA_NOT_EXIST, "Api配置[" + req.getApiCode() + "]不存在");
            }

            log.info("success: AccountService.getDomainControl(req = {}) 域控方式为: 信息范围和数据范围跟随Api配置的PowerCode; userPowerCode = {}", req, api.getPowerCode());
            usePowerCode = api.getPowerCode();
        }

        CommonResult<UserDomainControlItemResp> resp = withNormalRolePowerStep(req, usePowerCode, true);
        log.info("domain info [getDomainControl]: AccountService.getDomainControl() withNormalRolePowerStep(req = {}, usePowerCode = {}), resp = {}", req, usePowerCode, resp);
        return resp;
    }

    /**
     * PowerApi settingType检验
     *
     * @param settingType Integer
     * @return IErrorCode
     */
    private IErrorCode checkSettingType(Integer settingType) {
        if (settingType.equals(Constants.POWER_API_SETTING_TYPE_BY_DEFAULT) || settingType.equals(Constants.POWER_API_SETTING_TYPE_BY_API_BIND_POWER_CODE) || settingType.equals(Constants.POWER_API_SETTING_TYPE_BY_PAGE_CODE)) {
            return ErrorCode.SUCCESS;
        }

        log.error("check settingType error: AccountService.getDomainControl(settingType = {})", settingType);
        return IErrorCodeFactory.create(ErrorCode.USER_DATA_INVALID.getCode(), "PowerApi.settingType invalid");
    }

    private CommonResult<UserDomainControlItemResp> withApiStepWrap(UserDomainControlListReq req) {
        // step-1.1 没有直接配置power_api关系，走Api默认绑定权限码逻辑
        Api api = apiService.getOneByCode(req.getApiCode());
        if (api == null) {
            log.error("domain error [withApiStepWrap]: AccountService.withApiStepWrap(req = {}) apiService.getOneByCode(apiCode = {}) Api配置不存在", req, req.getApiCode());
            return CommonResult.failed(ErrorCode.DATA_NOT_EXIST, "Api配置[" + req.getApiCode() + "]不存在");
        }

        String usePowerCode;

        // Api未绑定归属页面的权限码
        if (api.getPowerCode() == null || api.getPowerCode().isEmpty()) {
            log.info("domain info [withApiStepWrap]: AccountService.withApiStepWrap(req = {}) 本意走ApiCode, ApiCode没有配置，则强制使用页面PowerCode", req);
            usePowerCode = req.getPowerCode();
        } else {
            usePowerCode = api.getPowerCode();
        }

        if (usePowerCode == null || usePowerCode.isEmpty()) {
            // 没有查询到powerCode，走默认通用配置
            UserDomainControlListReq domainControlReq = new UserDomainControlListReq();
            domainControlReq.setUserId(req.getUserId());
            domainControlReq.setApiCode(req.getApiCode());
            log.info("domain info [withApiStepWrap]: AccountService.withGlobalDefaultRolePower(domainReq = {}) error: 从角色权限和Api默认权限码角度均无法找到powerCode, 走全局默认配置", domainControlReq);
            return CommonResult.success(withGlobalDefaultRolePower(domainControlReq));
        }

        CommonResult<UserDomainControlItemResp> resp = withNormalRolePowerStep(req, usePowerCode, true);
        log.info("domain info [withApiStepWrap]: AccountService.withApiStepWrap() withNormalRolePowerStep({}, {}), resp = {}", req, usePowerCode, resp);
        return resp;
    }

    /**
     * 从user->role->role_power关系获取infoLevel和dataLevel配置并组装数据
     *
     * @param req          UserDomainControlReq
     * @param usePowerCode String
     * @return UserDomainControlItemResp
     */
    private CommonResult<UserDomainControlItemResp> withNormalRolePowerStep(UserDomainControlListReq req, String usePowerCode, boolean withThrowException) {
        log.info("accountService.withNormalRolePowerStep(req = {}, usePowerCode = {}) 正常角色权限计算逻辑", req, usePowerCode);
        Account account = checkAndReturnAccount(req.getUserId());
        Optional.ofNullable(usePowerCode).ifPresent(req::setPowerCode);
        List<RolePower> rolePowers = accountMapper.getDomainControl(req);
        if (rolePowers == null || rolePowers.isEmpty()) {
            log.error("error(FORBIDDEN): AccountService.withApiStepWrap(req = {}, usePowerCode = {}) 用户(id = {}) 暂无权限访问(未配置rolePower)", req, usePowerCode, req.getUserId());
            if (withThrowException) {
                throw new ApiException(ErrorCode.FORBIDDEN);
            } else {
                return CommonResult.failed(ErrorCode.VALIDATE_FAILED);
            }
        }

        List<String> rolePowerIds = new ArrayList<>();
        for (RolePower rolePower : rolePowers) {
            if (rolePower.getDataLevel() != null && rolePower.getDataLevel().equals(DataLevelEnum.DATA_LEVEL_SELECT_DEPT.getDataLevel())) {
                rolePowerIds.add(rolePower.getId());
            }
        }

        // 计算信息范围和数据范围
        UserDomainControlDataLevelItem dataLevel = initDataLevel();
        Map<String, String> selfAndSubDataLevel = new HashMap<>();
        Map<String, String> onlySelfDataLevel = new HashMap<>();
        Map<String, String> authDeptDataLevel = new HashMap<>();

        // 本部门及子部门信息范围
        List<UserDomainControlDeptInfoLevelItem> deptInfoLevel;
        List<String> commonC = new ArrayList<>();
        Map<String, String> commonMap = new HashMap<>();
        List<String> secretC = new ArrayList<>();
        Map<String, String> secretMap = new HashMap<>();
        Map<String, String> businessMap = new HashMap<>();
        List<String> businessC = new ArrayList<>();
        List<String> deptInfoLevelDeptIds = new ArrayList<>();
        Map<String, String> deptInfoLevelDeptIdsMap = new HashMap<>();

        // 数据范围为所有时的信息范围合并结果
        List<Integer> allInfoLevel = initAllInfoLevel();

        // 数据范围为仅自己时的信息范围合并结果
        List<UserDomainControlDeptInfoLevelItem> onlySelfDeptInfoLevel;
        List<String> onlySelfCommonC = new ArrayList<>();
        List<String> onlySelfSecretC = new ArrayList<>();
        List<String> onlySelfBusinessC = new ArrayList<>();
        List<String> onlySelfDeptInfoLevelDeptIds = new ArrayList<>();
        Map<String, String> onlySelfSecretMap = new HashMap<>();
        Map<String, String> onlySelfCommonMap = new HashMap<>();
        Map<String, String> onlySelfBusinessMap = new HashMap<>();
        Map<String, String> onlySelfDeptInfoLevelDeptIdsMap = new HashMap<>();

        List<String> curAndSubDeptIds = departmentService.searchCurrentAndSubDeptIds(Lists.newArrayList(account.getDeptId()));
        List<String> authDeptIds = powerAuthService.getAuthDeptIdsByRolePowerId(rolePowerIds);
        if (authDeptIds != null && !authDeptIds.isEmpty()) {
            authDeptIds = departmentService.searchCurrentAndSubDeptIds(authDeptIds);
        }
        List<String> onlySelfDeptIds = Lists.newArrayList(account.getDeptId());

        // 获取当前登录人员企业下部门及子部门
        String companyCode = account.getCompanyCode();
        String companyDeptId = "";
        if (!StringUtils.isEmpty(companyCode) && companyCode.equals(MemberCst.COMPANY_CODE_YXT)) {
            companyDeptId = MemberCst.DEPT_ID_YXT;
        } else {
            companyDeptId = MemberCst.DEPT_ID_CEIBS_ONLINE;
        }
        List<String> allCompanyDeptIds = departmentService.searchCurrentAndSubDeptIds(Lists.newArrayList(companyDeptId));

        for (RolePower v : rolePowers) {
            if (v.getDataLevel().equals(DataLevelEnum.DATA_LEVEL_ALL.getDataLevel())) {
                uniqueAddStringId(dataLevel.getAuthDeptIds(), allCompanyDeptIds, authDeptDataLevel);
                calDeptInfoLevelGroupByInfoLevel(commonC, secretC, businessC, v.getInfoLevel(), allCompanyDeptIds, commonMap, secretMap, businessMap);
                uniqueAddStringId(deptInfoLevelDeptIds, allCompanyDeptIds, deptInfoLevelDeptIdsMap);
            }

            if (v.getDataLevel().equals(DataLevelEnum.DATA_LEVEL_ONLY_DEPT_AND_SUB_DEPT.getDataLevel())) {
                uniqueAddStringId(dataLevel.getSelfAndSubDeptIds(), curAndSubDeptIds, selfAndSubDataLevel);
                calDeptInfoLevelGroupByInfoLevel(commonC, secretC, businessC, v.getInfoLevel(), curAndSubDeptIds, commonMap, secretMap, businessMap);
                uniqueAddStringId(deptInfoLevelDeptIds, curAndSubDeptIds, deptInfoLevelDeptIdsMap);
            }

            if (v.getDataLevel().equals(DataLevelEnum.DATA_LEVEL_ONLY_CREATOR_SELF.getDataLevel())) {
                uniqueAddStringId(dataLevel.getOnlySelfDeptIds(), onlySelfDeptIds, onlySelfDataLevel);
                calDeptInfoLevelGroupByInfoLevel(onlySelfCommonC, onlySelfSecretC, onlySelfBusinessC, v.getInfoLevel(), onlySelfDeptIds, onlySelfCommonMap, onlySelfSecretMap, onlySelfBusinessMap);
                uniqueAddStringId(onlySelfDeptInfoLevelDeptIds, onlySelfDeptIds, onlySelfDeptInfoLevelDeptIdsMap);
            }

            if (v.getDataLevel().equals(DataLevelEnum.DATA_LEVEL_SELECT_DEPT.getDataLevel())) {
                uniqueAddStringId(dataLevel.getAuthDeptIds(), authDeptIds, authDeptDataLevel);
                calDeptInfoLevelGroupByInfoLevel(commonC, secretC, businessC, v.getInfoLevel(), authDeptIds, commonMap, secretMap, businessMap);
                uniqueAddStringId(deptInfoLevelDeptIds, authDeptIds, deptInfoLevelDeptIdsMap);
            }
        }

        UserDomainControlItemResp resp = new UserDomainControlItemResp();
        resp.setPowerCode(req.getPowerCode());
        resp.setDataLevel(dataLevel);
        resp.setAllInfoLevel(allInfoLevel);

        // 计算自己及子部门/授权部门信息范围
        deptInfoLevel = calDeptInfoLevelItems(deptInfoLevelDeptIds, commonMap, secretMap, businessMap);
        resp.setDeptInfoLevel(deptInfoLevel);
        // 全部信息设置合并到自己及子部门
        mergerAllInfoLevelToSelfOfSub(deptInfoLevel, allInfoLevel);

        // 计算仅自己信息范围
        onlySelfDeptInfoLevel = calDeptInfoLevelItems(onlySelfDeptInfoLevelDeptIds, onlySelfCommonMap, onlySelfSecretMap, onlySelfBusinessMap);
        resp.setOnlySelfDeptInfoLevel(onlySelfDeptInfoLevel);
        // 全部信息设置合并到仅自己
        mergerAllInfoLevelToSelfOfSub(onlySelfDeptInfoLevel, allInfoLevel);

        // 将本部门及子部门信息范围数据合并到仅本部门信息范围上 并去掉登录者部门在本部门及子部门记录
        deptInfoLevel = mergeSelfAndSubToOnlySelfLevelInfo(account.getDeptId(), onlySelfDeptInfoLevel, deptInfoLevel);
        resp.setDeptInfoLevel(deptInfoLevel);

        return CommonResult.success(resp);
    }

    // 本部门及子部门信息范围数据合并到仅本部门信息范围上
    private List<UserDomainControlDeptInfoLevelItem> mergeSelfAndSubToOnlySelfLevelInfo(String accountDeptId, List<UserDomainControlDeptInfoLevelItem> onlySelfDeptInfoLevel, List<UserDomainControlDeptInfoLevelItem> deptInfoLevel) {
        if (onlySelfDeptInfoLevel == null || onlySelfDeptInfoLevel.isEmpty()) {
            return deptInfoLevel;
        }

        if (deptInfoLevel == null || deptInfoLevel.isEmpty()) {
            return deptInfoLevel;
        }

        Map<String, List<Integer>> mps = new HashMap<>();
        for (UserDomainControlDeptInfoLevelItem v : deptInfoLevel) {
            mps.put(v.getDeptId(), v.getInfoLevels());
        }

        boolean remove = false;
        for (UserDomainControlDeptInfoLevelItem v : onlySelfDeptInfoLevel) {
            if (mps.containsKey(v.getDeptId())) {
                if (v.getDeptId().equals(accountDeptId)) {
                    remove = true;
                }
                uniqueAddInfoLevel(v.getInfoLevels(), mps.get(v.getDeptId()), null);
            }
        }

        List<UserDomainControlDeptInfoLevelItem> resp = new ArrayList<>();
        for (UserDomainControlDeptInfoLevelItem v : deptInfoLevel) {
            if (remove && v.getDeptId().equals(accountDeptId)) {
                continue;
            }
            resp.add(v);
        }

        return resp;
    }

    // 合并所有范围设置的信息范围到仅自己&自己及子部门信息范围上
    private void mergerAllInfoLevelToSelfOfSub(List<UserDomainControlDeptInfoLevelItem> items, List<Integer> allInfoLevel) {
        if (allInfoLevel == null || allInfoLevel.isEmpty()) {
            return;
        }

        for (UserDomainControlDeptInfoLevelItem item : items) {
            List<Integer> itemInfoLevels = item.getInfoLevels();
            if (itemInfoLevels == null || itemInfoLevels.isEmpty()) {
                itemInfoLevels = new ArrayList<>();
            }
            uniqueAddInfoLevel(itemInfoLevels, allInfoLevel, null);
        }
    }

    /**
     * 计算部门维度信息范围
     *
     * @param deptInfoLevelDeptIds
     * @param commonMap
     * @param secretMap
     * @param businessMap
     * @return
     */
    private List<UserDomainControlDeptInfoLevelItem> calDeptInfoLevelItems(List<String> deptInfoLevelDeptIds, Map<String, String> commonMap, Map<String, String> secretMap, Map<String, String> businessMap) {
        List<UserDomainControlDeptInfoLevelItem> resp = new ArrayList<>();
        for (String deptId : deptInfoLevelDeptIds) {
            UserDomainControlDeptInfoLevelItem subItem = new UserDomainControlDeptInfoLevelItem();
            subItem.setDeptId(deptId);
            List<Integer> subInfoLevels = new ArrayList<>();
            if (commonMap.containsKey(deptId)) {
                subInfoLevels.add(InfoLevel.INFO_LEVEL_COMMON.getInfoLevel());
            }
            if (secretMap.containsKey(deptId)) {
                subInfoLevels.add(InfoLevel.INFO_LEVEL_SECRET.getInfoLevel());
            }
            if (businessMap.containsKey(deptId)) {
                subInfoLevels.add(InfoLevel.INFO_LEVEL_BUSINESS.getInfoLevel());
            }
            subItem.setInfoLevels(subInfoLevels);
            resp.add(subItem);
        }
        return resp;
    }

    /**
     * key(信息范围) => value(部门id)
     *
     * @param commonC      List<String>
     * @param secretC      List<String>
     * @param businessC    List<String>
     * @param infoLevelStr String
     * @param addDeptIds   List<String>
     * @param commonMap    Map<String, String>
     * @param secretMap    Map<String, String>
     * @param businessMap  Map<String, String>
     */
    private void calDeptInfoLevelGroupByInfoLevel(List<String> commonC, List<String> secretC, List<String> businessC, String infoLevelStr, List<String> addDeptIds, Map<String, String> commonMap, Map<String, String> secretMap, Map<String, String> businessMap) {
        List<Integer> infoLevels = getInfoLevelListByStr(infoLevelStr);
        if (!infoLevels.isEmpty()) {
            for (Integer v : infoLevels) {
                if (v.equals(InfoLevel.INFO_LEVEL_COMMON.getInfoLevel())) {
                    uniqueAddStringId(commonC, addDeptIds, commonMap);
                }

                if (v.equals(InfoLevel.INFO_LEVEL_SECRET.getInfoLevel())) {
                    uniqueAddStringId(secretC, addDeptIds, secretMap);
                }

                if (v.equals(InfoLevel.INFO_LEVEL_BUSINESS.getInfoLevel())) {
                    uniqueAddStringId(businessC, addDeptIds, businessMap);
                }
            }
        }
    }

    /**
     * 初始化数据范围为所有
     *
     * @return UserDomainControlDataLevelItem
     */
    private UserDomainControlDataLevelItem initDataLevel() {
        UserDomainControlDataLevelItem dataLevel = new UserDomainControlDataLevelItem();
        dataLevel.setSelfAndSubDeptIds(new ArrayList<>());
        dataLevel.setOnlySelfDeptIds(new ArrayList<>());
        dataLevel.setAuthDeptIds(new ArrayList<>());
        return dataLevel;
    }

    private List<Integer> initAllInfoLevel() {
        return new ArrayList<>();
    }

    /**
     * 查询不到数据权限，走默认逻辑
     * warning: 全局默认，不给使用方返回部门数据(数据范围为全部)，信息范围为全部(通用+敏感+商务)
     *
     * @param req UserDomainControlReq
     * @return UserDomainControlItemResp
     */
    private UserDomainControlItemResp withGlobalDefaultRolePower(UserDomainControlListReq req) {
        UserDomainControlItemResp resp = new UserDomainControlItemResp();
        Optional.ofNullable(req.getPowerCode()).ifPresent(resp::setPowerCode);
        resp.setAllInfoLevel(Lists.newArrayList(InfoLevel.INFO_LEVEL_COMMON.getInfoLevel(), InfoLevel.INFO_LEVEL_SECRET.getInfoLevel(), InfoLevel.INFO_LEVEL_BUSINESS.getInfoLevel()));
        resp.setDataLevel(new UserDomainControlDataLevelItem());
        resp.setDeptInfoLevel(Lists.newArrayList());
        resp.setOnlySelfDeptInfoLevel(new ArrayList<>());
        return resp;
    }

    /**
     * 获取power&api配置
     *
     * @param req UserDomainControlReq
     * @return PowerApi
     */
    private PowerApi searchPowerApi(UserDomainControlListReq req) {
        SearchPowerApiDto searchPowerApiDto = new SearchPowerApiDto();
        searchPowerApiDto.setApiCodes(Lists.newArrayList(req.getApiCode()));
        searchPowerApiDto.setPowerCodes(Lists.newArrayList(req.getPowerCode()));
        searchPowerApiDto.setIsDelete(Lists.newArrayList(Constants.IS_DELETE_NORMAL));
        List<PowerApi> powerApis = powerApiService.searchList(searchPowerApiDto);
        if (powerApis == null || powerApis.isEmpty()) {
            return null;
        }

        return powerApis.get(0);
    }

    private List<Integer> getInfoLevelListByStr(String infoLevel) {
        if (infoLevel == null || infoLevel.isEmpty()) {
            return new ArrayList<>();
        }

        String[] infoLevels = infoLevel.split(",");
        List<Integer> resp = new ArrayList<>();
        for (String level : infoLevels) {
            resp.add(new Integer(level));
        }
        return resp;
    }

    private void uniqueAddInfoLevel(List<Integer> c, List<Integer> addIntegerIds, Map<Integer, Integer> kvs) {
        if (addIntegerIds == null || addIntegerIds.isEmpty()) {
            return;
        }

        if (kvs == null) {
            kvs = new HashMap<>();
            if (c != null && !c.isEmpty()) {
                for (Integer vv : c) {
                    kvs.put(vv, vv);
                }
            }
        }

        if (c == null) {
            c = new ArrayList<>();
        }

        for (Integer addIntegerId : addIntegerIds) {
            if (!kvs.containsKey(addIntegerId)) {
                if (addIntegerId == null) {
                    return;
                }
                c.add(addIntegerId);
                kvs.put(addIntegerId, addIntegerId);
            }
        }
    }

    private void uniqueAddStringId(List<String> c, List<String> addStringIds, Map<String, String> kvs) {
        if (c == null) {
            c = new ArrayList<>();
        }

        if (kvs == null) {
            kvs = new HashMap<>();
            if (!c.isEmpty()) {
                for (String ss : c) {
                    kvs.put(ss, ss);
                }
            }
        }

        if (addStringIds == null || addStringIds.isEmpty()) {
            return;
        }

        for (String addStringId : addStringIds) {
            if (!kvs.containsKey(addStringId)) {
                if (addStringId == null || addStringId.isEmpty()) {
                    return;
                }
                c.add(addStringId);
                kvs.put(addStringId, addStringId);
            }
        }
    }

    /**
     * 超级管理员权限不受域控
     *
     * @return CommonResult<List < UserDomainControlItemResp>>
     */
    private CommonResult<UserDomainControlItemResp> getSuperDomainControl(UserDomainControlListReq param) {
        UserDomainControlItemResp resp = new UserDomainControlItemResp();
        Optional.ofNullable(param.getPowerCode()).ifPresent(resp::setPowerCode);
        resp.setDataLevel(new UserDomainControlDataLevelItem());
        resp.setDeptInfoLevel(new ArrayList<>());
        resp.setOnlySelfDeptInfoLevel(new ArrayList<>());
        resp.setAllInfoLevel(Lists.newArrayList(InfoLevel.INFO_LEVEL_COMMON.getInfoLevel(), InfoLevel.INFO_LEVEL_SECRET.getInfoLevel(), InfoLevel.INFO_LEVEL_BUSINESS.getInfoLevel()));
        return CommonResult.success(resp);
    }

    private ErrorCode checkMultiDomainControl(UserDomainControlListMultiReq req) {
        if (req == null) {
            return ErrorCode.VALIDATE_FAILED;
        }

        if (req.getUserId() == null || req.getUserId().isEmpty()) {
            return ErrorCode.VALIDATE_FAILED;
        }

        if (req.getPowerCodes() == null || req.getPowerCodes().isEmpty()) {
            return ErrorCode.VALIDATE_FAILED;
        }

        return ErrorCode.SUCCESS;
    }

    private ErrorCode checkDomainControl(UserDomainControlListReq req, boolean withApiCode) {
        if (req == null) {
            return ErrorCode.VALIDATE_FAILED;
        }

        if (req.getUserId() == null || req.getUserId().isEmpty()) {
            return ErrorCode.VALIDATE_FAILED;
        }

        if (withApiCode && (req.getApiCode() == null || req.getApiCode().isEmpty())) {
            return ErrorCode.VALIDATE_FAILED;
        }

        // 没有ApiCode则验证PowerCode
        if (!withApiCode && (req.getPowerCode() == null || req.getPowerCode().isEmpty())) {
            return ErrorCode.VALIDATE_FAILED;
        }

        return ErrorCode.SUCCESS;
    }

    public InitUserDto getInitUserById(String userId) {
        List<UserDetailResp> rs = accountMapper.getMultiUserDetail(Lists.newArrayList(userId));
        if (rs == null || rs.isEmpty()) {
            log.error("AccountService.getInitUserById(userId = {}) error: 用户不存在", userId);
            return null;
        }

        UserDetailResp detail = rs.get(0);
        InitUserDto resp = new InitUserDto();
        BeanUtils.copyProperties(detail, resp);
        resp.setUserId(detail.getId());
        Optional.ofNullable(detail.getLoginName()).ifPresent(resp::setLoginName);
        Optional.ofNullable(detail.getShowName()).ifPresent(resp::setUsername);
        if (detail.getRoles() != null && !detail.getRoles().isEmpty()) {
            List<String> roleIds = new ArrayList<>();
            for (RoleItemResp it : detail.getRoles()) {
                roleIds.add(it.getId());
            }
            resp.setRoleIds(roleIds);
        }

        return resp;
    }


    /**
     * 原始密码加密
     *
     * @param originPwd String
     * @return String
     */
    public String singOriginPwd(String originPwd) {
        String half = Base64.getEncoder().encodeToString((originPwd + MemberCst.PWD_SUFFIX).getBytes());
        return Base64.getEncoder().encodeToString((MemberCst.PWD_PREFIX + half).getBytes());
    }



    /**
     * 重置密码（管理员）
     *
     * @return CommonResult
     */
    public CommonResult<VoidResp> resetPwd(UserResetPwdReq req, InitUserDto initUserDto) {

        InitUserDto init=new InitUserDto();
        if (initUserDto == null || initUserDto.getUserId() == null) {
            return CommonResult.failed(ErrorCode.UNAUTHORIZED);
        }

        if (req == null || req.getUserId() == null || req.getUserId().isEmpty() || req.getPwd() == null || req.getPwd().isEmpty()) {
            return CommonResult.failed(ErrorCode.VALIDATE_FAILED);
        }


//        if (!accountRoleService.isSuperUser(initUserDto.getUserId())) {
//            return CommonResult.failed(IErrorCodeFactory.create(ErrorCode.FORBIDDEN.getCode(), "重置密码功能仅限超管使用"));
//        }

        Account account = getById(req.getUserId());
        if (account == null) {
            return CommonResult.failed(ErrorCode.USER_NOT_EXIST);
        }
        // reset pwd
        try {
            String originPwd = req.getPwd();
            String salt = createSalt();
            String inputSecretPwd = signSecretPwd(account.getLoginName(), originPwd, salt);
            account.setPwd(inputSecretPwd);
            account.setSalt(salt);
            boolean outcome= updateById(account);
            init.setUserId(req.getUserId());
            if(outcome){
                logout(init);
            }
            addSysLogUtil.addLog(initUserDto, LogTypeEnum.LOG_TYPE_ADD.getTypeNum(), LogModuleEnum.LOG_MODULE_USER.getModuleNum(),"重置"+req.getUserId(),req);
            log.info("重置密码: AccountService.resetPwd(req = {}, initUserId = {}) successfully! newInputSecretPwd = {}, newSalt = {}", req, initUserDto, inputSecretPwd, salt);
            return CommonResult.success(new VoidResp());
        } catch (Exception e) {
            log.error("error: AccountService.validSecretPwd({}) 密码解密异常", req.getPwd());
            return CommonResult.failed(ErrorCode.USER_PWD_UNPACK_INVALID);
        }

    }


    /**
     * 重置密码
     *
     * @return CommonResult
     */
    public CommonResult<VoidResp> updatePwd(UserUpdatePwdReq req, InitUserDto initUserDto) {
        if (initUserDto == null || initUserDto.getUserId() == null) {
            return CommonResult.failed(ErrorCode.UNAUTHORIZED);
        }
        Account account = getById(initUserDto.getUserId());
        if (StringUtils.isEmpty( account)) {
            return CommonResult.failed(ErrorCode.USER_NOT_EXIST);
        }
        // reset pwd
        try {
            String originPwd = req.getPwd();
            String salt = createSalt();
            String inputSecretPwd = signSecretPwd(account.getLoginName(), originPwd, salt);
            account.setPwd(inputSecretPwd);
            account.setSalt(salt);
            updateById(account);
            log.info("重置密码: AccountService.resetPwd(req = {}, initUserId = {}) successfully! newInputSecretPwd = {}, newSalt = {}", req, initUserDto, inputSecretPwd, salt);
            return CommonResult.success(new VoidResp());
        } catch (Exception e) {
            log.error("error: AccountService.validSecretPwd({}) 密码解密异常", req.getPwd());
            return CommonResult.failed(ErrorCode.USER_PWD_UNPACK_INVALID);
        }

    }
    /**
     * 用户域控列表
     *
     * @param req UserDomainControlListReq
     * @return CommonResult<UserDomainControlListResp>
     */
    public CommonResult<UserDomainControlListResp> getUserDomainList(UserDomainControlListReq req) {
        ErrorCode check = checkDomainControl(req, true);
        if (ErrorReport.hasError(check)) {
            return CommonResult.failed(check);
        }

        CommonResult<UserDomainControlItemResp> domainResp = getDomainControl(req);
        if (domainResp == null) {
            return CommonResult.success(new UserDomainControlListResp());
        }

        UserDomainControlItemResp vo = domainResp.getData();
        if (vo == null || vo.getDataLevel() == null) {
            return CommonResult.success(new UserDomainControlListResp());
        }

        // 数据权限范围
        UserDomainControlDataLevelItem domainControl = vo.getDataLevel();
        UserDomainControlListResp resp = new UserDomainControlListResp();
        List<UserDomainControlListItemResp> data;
        if (StringUtil.isEmpty(domainControl.getSelfAndSubDeptIds()) && StringUtil.isEmpty(domainControl.getOnlySelfDeptIds()) && StringUtil.isEmpty(domainControl.getAuthDeptIds())) {
            // 不受域控
            resp.setIsDomainControl(0);
        } else {
            resp.setIsDomainControl(1);
        }
        data = accountMapper.getDomainControlUserList(req, domainControl, accountRoleService.isSuperUser(req.getUserId()) ? 1 : 0);
        Optional.ofNullable(data).ifPresent(resp::setData);
        return CommonResult.success(resp);
    }

    /**
     * 用户部门
     *
     * @param req         UserBindDeptReq
     * @param initUserDto InitUserDto
     * @return CommonResult<VoidResp>
     */
    public CommonResult<VoidResp> bindDept(UserBindDeptReq req, InitUserDto initUserDto) {
        if (StringUtils.isEmpty(req)) {
            return ErrorReport.fail(IErrorCodeFactory.create(ErrorCode.PARAM_INVALID.getCode(), "参数不能为空"));
        }

        if (StringUtils.isEmpty(req.getAccountId())) {
            return ErrorReport.fail(IErrorCodeFactory.create(ErrorCode.PARAM_INVALID.getCode(), "参数accountId不能为空"));
        }

        if (StringUtils.isEmpty(req.getDeptId())) {
            return ErrorReport.fail(IErrorCodeFactory.create(ErrorCode.PARAM_INVALID.getCode(), "参数deptId不能为空"));
        }

        Account account = getById(req.getAccountId());
        if (StringUtils.isEmpty(account)) {
            return CommonResult.failed(ErrorCode.USER_NOT_EXIST);
        }

        log.info("bindDept(req = {}, initUserDto = {}) before update account = {}", req, initUserDto, account);
        accountMapper.update(account,new UpdateWrapper<Account>().lambda()
                .eq(Account::getId,req.getAccountId())
                .set(Account::getDeptId,req.getDeptId()));
        addSysLogUtil.addLog(initUserDto, LogTypeEnum.LOG_TYPE_ADD.getTypeNum(), LogModuleEnum.LOG_MODULE_USER.getModuleNum(),"绑定部门"+req.getAccountId(),req);
        return CommonResult.success(new VoidResp());
    }

    /**
     * 反查指定部门和powerCode权限码对应的所有用户id
     *
     * @param createDeptId 指定创建者部门
     * @param powerCode  指定权限码
     * List<String>
     */
    public List<String> getDomainAuthDeptUserIds(String createDeptId, String powerCode) {
        SearchRolePowerDto dto = new SearchRolePowerDto();
        dto.setPowerCodes(Lists.newArrayList(powerCode));
        dto.setIsDelete(Lists.newArrayList(Constants.IS_DELETE_NORMAL));
        List<RolePower> rolePowers = rolePowerService.searchList(dto);
        if (CollectionUtils.isEmpty(rolePowers)) {
            return new ArrayList<>();
        }

        List<String> roleIds = new ArrayList<>();
        for (RolePower v : rolePowers) {
            if (MemberCst.DATA_LEVEL_SELECT_DEPT == v.getDataLevel()) {
                QueryWrapper<PowerAuthDept> q = new QueryWrapper<>();
                q.eq("role_power_id", v.getId());
                q.eq(Constants.FIELD_KEY_IS_DELETE, Constants.IS_DELETE_NORMAL);
                List<PowerAuthDept> authItems = powerAuthDeptMapper.selectList(q);
                if (CollectionUtils.isEmpty(authItems)) {
                    continue;
                }

                for (PowerAuthDept authItem : authItems) {
                    List<String> authDeptIds = departmentService.searchCurrentAndSubDeptIds(Lists.newArrayList(authItem.getAuthDeptId()));
                    if (authDeptIds != null && authDeptIds.contains(createDeptId)) {
                        roleIds.add(v.getRoleId());
                        break;
                    }
                }
            }
        }

        if (CollectionUtils.isEmpty(roleIds)) {
            return new ArrayList<>();
        }

        roleIds = roleIds.stream().distinct().collect(Collectors.toList());
        QueryWrapper<AccountRole> ar = new QueryWrapper<>();
        ar.in("role_id", roleIds);
        ar.eq(Constants.FIELD_KEY_IS_DELETE, Constants.IS_DELETE_NORMAL);
        List<AccountRole> accountRoles = accountRoleMapper.selectList(ar);
        if (CollectionUtils.isEmpty(accountRoles)) {
            return new ArrayList<>();
        }

        List<String> userIds = new ArrayList<>();
        for (AccountRole accountRole : accountRoles) {
            userIds.add(accountRole.getAccountId());
        }
        userIds = userIds.stream().distinct().collect(Collectors.toList());
        return userIds;
    }
}
